package com.personalkeeper;

import static com.personalkeeper.PersonalKeeper.ADDRESS;
import static com.personalkeeper.PersonalKeeper.DESCRIPTION;
import static com.personalkeeper.PersonalKeeper.LATITUDE;
import static com.personalkeeper.PersonalKeeper.LONGITUDE;
import static com.personalkeeper.PersonalKeeper.REQUEST;
import static com.personalkeeper.PersonalKeeper.TITLE;
import static com.personalkeeper.PersonalKeeper.NAME;
import static com.personalkeeper.PersonalKeeper.DATE;
import static com.personalkeeper.PersonalKeeper.LOCATION;

import java.util.ArrayList;
import java.util.Date;

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.database.Cursor;
import android.location.Location;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import com.google.android.maps.GeoPoint;
import com.personalkeeper.HistoryProvider.Markers;
import com.personalkeeper.HistoryProvider.TrackingSession;
import com.personalkeeper.PersonalKeeper.Utils;


public class MapDisplay extends ClientMapActivity
    implements
    LocationServiceController.ConnectionStateListener,
    OnClickListener {

    private class AsyncMarkerLoader extends AsyncTask<Void, Void, ArrayList<Marker>> {

        @Override
        protected ArrayList<Marker> doInBackground(Void... params) {
            ArrayList<Marker> result = new ArrayList<Marker>();
            Cursor c = getContentResolver().query(
                           Markers.CONTENT_URI,
                           null,
                           null,
                           null,
                           null);
            try {
                int idID = c.getColumnIndexOrThrow(Markers._ID);
                int latitudeID = c.getColumnIndexOrThrow(Markers.LATITUDE);
                int longitudeID = c.getColumnIndexOrThrow(Markers.LONGITUDE);
                int dateID = c.getColumnIndexOrThrow(Markers.DATE);
                int nameID = c.getColumnIndexOrThrow(Markers.NAME);
                int addressID = c.getColumnIndexOrThrow(Markers.ADDRESS);
                if (c.moveToFirst()) {
                    do {
                        // TODO: collect Markers from database (now they contain requests)
                        // and process them somewhere
                        double id = c.getLong(idID);
                        double latitude = c.getDouble(latitudeID);
                        double longitude = c.getDouble(longitudeID);
                        long date = c.getLong(dateID);
                        String name = c.getString(nameID);
                        String address = c.getString(addressID);
                        String title = name == null ? address : name;
                        Marker marker = new Marker(Utils.createGeoPoint(latitude, longitude),
                                                   title, 
                                                   new Date(date).toString(), 
                                                   Marker.TYPE_CONTACT_POINT, 
                                                   MapDisplay.this);
                        result.add(marker);
                    } while (c.moveToNext() && !isCancelled());
                }
            } finally {
                c.close();
            }

            return result;
        }

        @Override
        protected void onPostExecute(ArrayList<Marker> result) {
            contactsLayer.clear();
            for (Marker marker: result)
                marker.setAssotiateContext(MapDisplay.this);
            contactsLayer.addOverlayRange(result);
            mIsLoadingPath = false;
            getContentResolver().delete(Markers.CONTENT_URI, null, null);
        }
    }

    private AsyncMarkerLoader mAsyncMarkerLoader;

    @Override
    protected void onStart() {
        super.onStart();
        // TODO:
        // AsyncMarkerLoader must also show dialog to user to ask if we need to 
        // send answer (yes, no, yes to all) we need to put same code in
        // onCreateDialog(int, Bundle) on REQUEST_FOR_YOUR_LOCATION
        try {
            mAsyncMarkerLoader = new AsyncMarkerLoader();
            mIsLoadingPath = true;
            mAsyncMarkerLoader.execute();
        } catch (Exception e) {
            e.printStackTrace(); // oops!
        }
    }
    @Override
    protected void onStop() {
        super.onStop();
        if (mAsyncMarkerLoader != null) {
            mAsyncMarkerLoader.cancel(true);
            mAsyncMarkerLoader = null;
        }
    }
    
    private boolean mIsLoadingPath = false;
    private ArrayList<Location> mLocationsToInsertAfterLoading = new ArrayList<Location>();
    
    @Override
    public void onLocationUpdate(Location location) {
        if (mIsLoadingPath)
            mLocationsToInsertAfterLoading.add(location);
        else {
            if (mLocationsToInsertAfterLoading.size() > 0) {
                for (Location loc : mLocationsToInsertAfterLoading)
                    super.onLocationUpdate(loc);
            }
            super.onLocationUpdate(location);
        }
    }

    @Override
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        smsSender = new SMSSender(this);

        notesLayer = new MarkersLayer(this.getResources().getDrawable(R.drawable.note));
        notesLayer.setOnTapListener(this);
        contactsLayer = new MarkersLayer(this.getResources().getDrawable(R.drawable.contact));
        contactsLayer.setOnTapListener(this);


        //adding overlays
        getMapOverlays().add(notesLayer);
        getMapOverlays().add(contactsLayer);
        getMap().invalidate();

        getGPSController().setConnectionStateListener(this);
    }

    // menu
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.map_menu, menu);
        return true;
    }
    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        MenuItem item = menu.findItem(R.id.showTracking);
        if (item != null) {
            if (isDrawingPath()) {
                item.setTitle(R.string.map_menu_stop_tracking);
            } else {
                item.setTitle(R.string.map_menu_start_tracking);
            }
        }
        return super.onPrepareOptionsMenu(menu);
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.showTracking:
            try {
                setDrawingPath(!isDrawingPath());
            } catch (Exception e) {
                e.printStackTrace();
            }
            return true;

        case R.id.notes:
            showDialog(NOTES_DIALOG);
            return true;

        case R.id.showStatistics:
            showDialog(STATISTICS_DIALOG);
            return true;

        case R.id.settings:
            Intent intent = new Intent(this, SettingsActivity.class);
            startActivity(intent);
            return true;

        case R.id.get_contact_location:
            Intent newIntent = new Intent(Intent.ACTION_GET_CONTENT);
            newIntent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
            startActivityForResult(newIntent, GET_CONTACTS_LOCATION_CODE);
            return true;

        case R.id.animate_to_current_point:
            setAnimateToCurrentPoint(true);
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    // dialogues
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == RESULT_OK && requestCode == GET_CONTACTS_LOCATION_CODE) {
            if (data != null) {
                try {
                    Uri uri = data.getData();
                    if (uri != null) {
                        String number = getContactNumberFromUri(uri);
                        if (number.length() > 0) {
                            Log.d(TAG, "number=" + number);
                            smsSender.sendSMS(number, getLastUpdatedLocation(), true);
                            Log.d(TAG, "sms sent");
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else
                Log.d(TAG, "data is null");
        }
    }
    @Override
    protected Dialog onCreateDialog(int id) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        Dialog dialog;
        switch (id) {
        case NOTES_DIALOG:
            builder.setTitle(getString(R.string.notes_dialog_choose_action));
            builder.setItems(R.array.NotesDialogItems, this);
            dialog = builder.create();
            break;

        case NO_LOCATION_UPDATES_DIALOG:
            builder.setIcon(android.R.drawable.ic_dialog_alert)
            .setMessage(getString(R.string.error_no_location_updates));
            dialog = builder.create();
            break;
            //need run activity on click which is on stats.xml
        case STATISTICS_DIALOG:
            builder.setTitle(getString(R.string.statistics_dialog_title));
            builder.setItems(R.array.settings_per_time, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int item) {
                    Date current = new Date();
                    switch (item) {
                    case 0:
                        current.setHours(current.getHours() - 1);
                        break;

                    case 1:
                        current.setDate(current.getDate() - 1);
                        break;

                    case 2:
                        current.setMonth(current.getMonth() - 1);
                        break;

                    case 3:
                        getContentResolver().delete(TrackingSession.CONTENT_URI, null, null);
                        return;

                    default:
                        return;
                    }
                    Intent intent = new Intent(getApplicationContext(), ShowStatsActivity.class);
                    intent.putExtra(DATE, current.getTime());
                    startActivity(intent);
                }
            });
            dialog = builder.create();
            break;

        default:
            dialog = super.onCreateDialog(id);
            break;
        }
        return dialog;
    }
    @Override
    protected Dialog onCreateDialog(int id, Bundle args) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        Dialog dialog;
        String yes = getString(android.R.string.yes);
        String no = getString(android.R.string.no);
        switch (id) {
            case CONTACT_LOCATION_RECEIVED: {
                double latitude = args.getDouble(LATITUDE);
                double longitude = args.getDouble(LONGITUDE);
                final GeoPoint point = Utils.createGeoPoint(latitude, longitude);
                Log.d(TAG, latitude + "/" + longitude);
    
                builder
                .setIcon(android.R.drawable.ic_dialog_alert)
                .setMessage(getString(R.string.contact_location_received))
                .setCancelable(true)
                .setPositiveButton(yes, new OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        setCentralPoint(point);
                    }
                })
                .setNegativeButton(no, new OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                    }
                });
                dialog = builder.create();
                break;
            }
            case REQUEST_FOR_YOUR_LOCATION: {
                String contactname = args.getString(NAME);
                final String address = args.getString(ADDRESS);
                builder.setIcon(android.R.drawable.ic_menu_help)
                       .setMessage(contactname + " " + getString(R.string.request_for_your_location))
                       .setCancelable(true)
                       .setPositiveButton(yes, new OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        try {
                            smsSender.sendSMS(address, getLastUpdatedLocation(), false);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                })
                .setNegativeButton(no, new OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                    }
                });
                dialog = builder.create();
                break;
            }
        default:
            dialog = super.onCreateDialog(id, args);
            break;
        }
        return dialog;
    }

    // events
    @Override
    public void onSMSReceived(Bundle smsInfo) {
        try {
            onSMSReceivedHelper(smsInfo);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public void onSMSReceivedHelper(Bundle smsInfo) {
        String address = smsInfo.getString(ADDRESS);
        //Date date = new Date(smsInfo.getLong(DATE));
        boolean request = smsInfo.getBoolean(REQUEST);
        double latitude = smsInfo.getDouble(LATITUDE);
        double longitude = smsInfo.getDouble(LONGITUDE);
        String contactName = smsInfo.getString(NAME);
        if (contactName == null)
            contactName = address;
        Bundle bundle;
        Log.d(TAG, "SMS Received");
        Log.d(TAG, String.valueOf(request));
        Log.d(TAG, address);
        if (!request) {
            // latitude and longitude initialzed not with 0.0
            Log.d(TAG, "set location");
            GeoPoint point = Utils.createGeoPoint(latitude, longitude);
            // TODO: create good marker
            Marker marker = new Marker(
                    point, 
                    contactName, 
                    Utils.presentableDate(System.currentTimeMillis()), 
                    Marker.TYPE_CONTACT_POINT, 
                    this);
            contactsLayer.addOverlay(marker);
            getMap().invalidate(marker.getMarker(0).getBounds());
            bundle = new Bundle();
            bundle.putDouble(LATITUDE, latitude);
            bundle.putDouble(LONGITUDE, longitude);
            showDialog(CONTACT_LOCATION_RECEIVED, bundle);
        } else {
            if (smsInfo.containsKey(LATITUDE) && smsInfo.containsKey(LONGITUDE)) {
                GeoPoint point = Utils.createGeoPoint(latitude, longitude);
                Marker marker = new Marker(
                        point, 
                        contactName, 
                        Utils.presentableDate(System.currentTimeMillis()), 
                        Marker.TYPE_CONTACT_POINT, 
                        this);
                contactsLayer.addOverlay(marker);
            } else
                Log.d(TAG, "received sms contains no location to show");
            
            if (getLastUpdatedLocation() != null) {
                Log.d(TAG, "get location");
                bundle = new Bundle();
                bundle.putString(NAME, contactName);
                bundle.putString(ADDRESS, address);
                showDialog(REQUEST_FOR_YOUR_LOCATION, bundle);
            }
            // else TODO: oops, no location to send
        }
    }
    @Override
    public void onClick(DialogInterface dialog, int item) {
        Intent intent;
        if (item == 0) { // Add note
            intent = new Intent(this, CreateNoteActivity.class);
            if (getLastUpdatedLocation() != null) {
                intent.putExtra(LOCATION, getLastUpdatedLocation());
                startActivity(intent);
            } else
                showDialog(NO_LOCATION_UPDATES_DIALOG);
        } else if (item == 1) { // view notes
            intent = new Intent(this, NotesListActivity.class);
            startActivity(intent);
        }
    }
    
    
    @Override
    public void onNewIntent(Intent newIntent) {
        try {
            onNewIntentHelper(newIntent);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public void onNewIntentHelper(Intent newIntent) {
        super.onNewIntent(newIntent);
        Log.d(TAG, "onNewIntent");
        Bundle extras = newIntent.getExtras();
        String action = newIntent.getAction();
        if (action != null && action.equals(PersonalKeeper.ACTION_SHOW_NOTE)) {
            if (extras != null &&
                extras.containsKey(LATITUDE) &&
                extras.containsKey(LONGITUDE) &&
                extras.containsKey(TITLE) &&
                extras.containsKey(DESCRIPTION) &&
                extras.containsKey(DATE)) {
                Double latitude = extras.getDouble(LATITUDE);
                Double longitude = extras.getDouble(LONGITUDE);
                String title = extras.getString(TITLE);
                String description = extras.getString(DESCRIPTION);
                GeoPoint point = Utils.createGeoPoint(latitude, longitude);
                Marker marker = new Marker(point, title, description, Marker.TYPE_LOCATION_POINT, this);
                notesLayer.addOverlay(marker);
                getMap().invalidate(marker.getMarker(0).getBounds());
                setCentralPoint(point);
            } else {
                //Toast.makeText(this, "no note", Toast.LENGTH_LONG).show();
            }
        } else if (action != null && action.equals(PersonalKeeper.ACTION_REQUEST_FOR_LOCATION)) {
            if (extras != null) {
                this.onSMSReceived(extras);
            }
        }
    }

    // save & restore
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        outState.putParcelableArrayList(SAVE_KEY_NOTES_DISPLAYED, notesLayer.getItems());
        outState.putParcelableArrayList(SAVE_KEY_CONTACTS_DISPLAYED, contactsLayer.getItems());
    }
    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        if (savedInstanceState != null) {
            ArrayList<Marker> markers;
            markers = savedInstanceState.getParcelableArrayList(SAVE_KEY_NOTES_DISPLAYED);
            if (markers != null) {
                for (Marker marker: markers) {
                    marker.setAssotiateContext(this);
                    notesLayer.clear();
                    notesLayer.addOverlay(marker);
                }
            }
            markers = savedInstanceState.getParcelableArrayList(SAVE_KEY_CONTACTS_DISPLAYED);
            if (markers != null) {
                for (Marker marker : markers) {
                    marker.setAssotiateContext(this);
                    contactsLayer.clear();
                    contactsLayer.addOverlay(marker);
                }
            }
        }
    }

    private String getContactNumberFromUri(Uri uri) {
        Cursor c = getContentResolver().query(
                       uri,
                       new String[] { ContactsContract.CommonDataKinds.Phone.NUMBER },
                       null,
                       null,
                       null);
        if (c != null) {
            try {
                if (c.moveToFirst())
                    return c.getString(0);
            } finally {
                c.close();
            }
        }
        return null;
    }


    @Override
    public void onServiceConnected() {
        if (mConnectionLostDialog != null) {
            mConnectionLostDialog.dismiss();
            mConnectionLostDialog = null;
        }
    }

    @Override
    public void onServiceDisconnected() {
        String title = getString(R.string.internal_problem_occurred);
        String descrition = getString(R.string.fixing_internal_problem);
        mConnectionLostDialog = ProgressDialog.show(this, title, descrition, true, false);
    }

    
    private static final String SAVE_KEY_CONTACTS_DISPLAYED = "com.personalkeeper.contactsdisplayed";
    private static final String SAVE_KEY_NOTES_DISPLAYED = "com.personalkeeper.notesdisplayed";
    
    private static final int NOTES_DIALOG = 1;
    private static final int NO_LOCATION_UPDATES_DIALOG = 2;
    private static final int CONTACT_LOCATION_RECEIVED = 3;
    private static final int REQUEST_FOR_YOUR_LOCATION = 4;
    private static final int STATISTICS_DIALOG = 5;
    private static final int GET_CONTACTS_LOCATION_CODE = 1000;

    private MarkersLayer notesLayer;
    private MarkersLayer contactsLayer;
    private SMSSender smsSender;
    private Dialog mConnectionLostDialog;

    private static final String TAG = MapDisplay.class.toString();
}

